/*
 * #%L
 * Client
 * %%
 * Copyright (C) 2011 Ractoc.com
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.ractoc.opengamefinder.client.plugins;

import com.ractoc.opengamefinder.client.plugins.messages.CacheByteArrayMessage;
import com.ractoc.opengamefinder.client.plugins.messages.GetCachedFilenameMessage;
import com.ractoc.opengamefinder.client.plugins.results.GetCachedFilenameMessageResult;
import com.ractoc.opengamefinder.api.results.MessageResultsErrorCodes;
import com.ractoc.opengamefinder.client.plugins.results.CacheByteArrayMessageResult;
import com.ractoc.pffj.api.BasePluginMessage;
import com.ractoc.pffj.api.BasePluginMessageResult;
import com.ractoc.pffj.api.PluginLoaderException;
import com.ractoc.pffj.api.PluginMessage;
import com.ractoc.pffj.api.PluginMessageResultListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * This is a utility class for storing stuff in the cache and for retreiving it
 * from the cache.
 * 
 * @author ractoc
 * @version 0.1
 */
public final class CachePlugin extends BaseClientPlugin {

    private static Logger logger = Logger.getLogger(CachePlugin.class.getName());
    private String cacheFolder;

    /**
     * Default constructor.
     */
    public CachePlugin() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void load() throws PluginLoaderException {
        if (StringUtils.isEmpty(cacheFolder)) {
            throw new PluginLoaderException("Mandatory parameter, cacheFolder, not set.");
        }
    }

    /**
     * {@inheritDoc}
     * Currently supports the following Messages:
     * <ul>
     * <li>GetCachedFilenameMessage</li>
     * <li>CacheByteArrayMessage</li>
     * </ul>
     */
    @Override
    public void processMessage(PluginMessage msg, PluginMessageResultListener pmrListener) {
        BasePluginMessageResult result = null;
        if (msg instanceof GetCachedFilenameMessage) {
            result = getCachedFileName((BasePluginMessage) msg);
        } else if (msg instanceof CacheByteArrayMessage) {
            result = cacheByteArray((BasePluginMessage) msg);
        }
        pmrListener.receivedResult(result);
    }

    private BasePluginMessageResult getCachedFileName(BasePluginMessage msg) {
        GetCachedFilenameMessageResult gcfResult = new GetCachedFilenameMessageResult();
        GetCachedFilenameMessage gcfMsg = (GetCachedFilenameMessage) msg;
        gcfResult.setErrorCode(MessageResultsErrorCodes.SUCCESS);
        String filename = determineFilename(gcfMsg.getFileId(), gcfMsg.getVersion(), gcfMsg.getType());
        logger.debug("detecting the presence of cached file " + filename);
        File cachedFile = new File(filename);
        if (!cachedFile.exists()) {
            gcfResult.setErrorCode(MessageResultsErrorCodes.CACHED_FILE_NOT_FOUND);
        }
        gcfResult.setFilename(filename);
        return gcfResult;
    }

    private BasePluginMessageResult cacheByteArray(BasePluginMessage msg) {
        CacheByteArrayMessage cbMsg = (CacheByteArrayMessage) msg;
        CacheByteArrayMessageResult cbResult = new CacheByteArrayMessageResult();
        String filename = determineFilename(cbMsg.getFileId(), cbMsg.getVersion(), cbMsg.getType());
        logger.debug("caching to file " + filename);
        // first we need to determine if there are any old versions of this file in the cache.
        File avatarFolderHandler = new File(cacheFolder);
        if (!avatarFolderHandler.exists()) {
            avatarFolderHandler.mkdirs();
        }
        
        final Integer fileId = cbMsg.getFileId();
        String[] fileList = avatarFolderHandler.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String filename) {
                return filename.startsWith(fileId.toString() + ".");
            }
        });
        // an old cached file is found. This one needs to be removed first.
        File oldFile = null;
        if (fileList != null) {
            for (String oldFilename : fileList) {
                oldFile = new File(avatarFolderHandler, oldFilename);
                oldFile.delete();
            }
        }
        FileOutputStream fod = null;
        try {
            // now we can cache the new file.
            File newFile = new File(filename);
            fod = new FileOutputStream(newFile);
            fod.write(cbMsg.getToCache());
            fod.flush();
        }
        catch (IOException ex) {
            logger.error("Unable to create new cached file.", ex);
            cbResult.setErrorCode(MessageResultsErrorCodes.INTERNAL_ERROR);
        }
        finally {
            try {
                fod.close();
                cbResult.setFilename(filename);
            }
            catch (IOException ex) {
                logger.error("Unable to create new cached file.", ex);
                cbResult.setErrorCode(MessageResultsErrorCodes.INTERNAL_ERROR);
            }
        }
        return cbResult;
    }

    /**
     * Gets the cache folder to contain the downloaded content.
     * @return The cache folder.
     */
    public String getCacheFolder() {
        return cacheFolder;
    }

    /**
     * Sets the cache folder. This method is used from the PFfJ config file.
     * @param cacheFolder The cache folder as it appears in the config file.
     */
    public void setCacheFolder(String cacheFolder) {
        this.cacheFolder = cacheFolder;
    }

    private String determineFilename(Integer fileId, Integer version, String type) {
        return cacheFolder + fileId + "." + version + "." + type;
    }
}
